﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;

namespace Security
{
    /*
     * 这个类中已经实现了检测和计时功能,后续只需要处理满足报警条件后的逻辑功能即可,比如上报
     */
    class Check : Singleton<Check>
    {
        // 所有需要监控的进程列表.由配置文件加载
        public IList<ProcessInfo> checkProcesss = new List<ProcessInfo>();
        // 所有需要监控的进程字典.由列表分析组成
        private Dictionary<String, ProcessInfo> dictProcess = new Dictionary<string, ProcessInfo>();
        // 检测定时器,用于检测输入空闲时间,进程置于前台的时间
        System.Timers.Timer timerCheck;
        // 配置更新定时器,定时去重载cnf.json文件,文件的定时拉取依赖于ApplicationSecurityService服务
        System.Timers.Timer timerReloadConfig;
        // 创建结构体用于返回捕获时间
        [StructLayout(LayoutKind.Sequential)]
        struct LASTINPUTINFO
        {
            // 设置结构体块容量
            [MarshalAs(UnmanagedType.U4)]
            public int cbSize;
            // 捕获的时间
            [MarshalAs(UnmanagedType.U4)]
            public uint dwTime;
        }
        [DllImport("user32.dll")]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        ~Check()
        {
            timerCheck.Enabled = false;
            timerReloadConfig.Enabled = false;
        }

        public void Init()
        {
            // 启动检测定时器
            timerCheck = new System.Timers.Timer();
            timerCheck.Interval = 20000;  //设置计时器事件间隔执行时间
            timerCheck.Elapsed += new System.Timers.ElapsedEventHandler(timerCheck_Elapsed);
            timerCheck.Enabled = true;

            // 启动配置重载定时器
            timerReloadConfig = new System.Timers.Timer();
            timerReloadConfig.Interval = 600000;  //设置计时器事件间隔执行时间
            timerReloadConfig.Elapsed += new System.Timers.ElapsedEventHandler(timerReloadConfig_Elapsed);
            timerReloadConfig.Enabled = true;
        }

        // 获取键盘和鼠标没有操作的时间
        private static long GetLastInputTime()
        {
            LASTINPUTINFO vLastInputInfo = new LASTINPUTINFO();
            vLastInputInfo.cbSize = Marshal.SizeOf(vLastInputInfo);
            // 捕获时间
            if (!GetLastInputInfo(ref vLastInputInfo))
                return 0;
            else
                return Environment.TickCount - (long)vLastInputInfo.dwTime;
        }

        // 从配置加载类设置需要检测的进程信息
        internal void SetCheckProcess(IList<ProcessInfo> list)
        {
            checkProcesss = list;
            foreach (var prcessInfo in checkProcesss)
            {
                LogHelper.Instance.WriteLog("checkeItem:" + prcessInfo.ProcessName + " md5:" + prcessInfo.FileMd5Code);
                if(!dictProcess.ContainsKey(prcessInfo.ProcessName))
                {
                    dictProcess.Add(prcessInfo.ProcessName, prcessInfo);
                }
                else
                {
                    dictProcess[prcessInfo.ProcessName].IsRecoderActiveTime = prcessInfo.IsRecoderActiveTime;
                }
            }
        }

        // 检测鼠标键盘输入
        void CheckMouseKeyboard()
        {
            if(GetLastInputTime() >= 30) // 30秒
            {
                //触发报警操作,这里这句弹出框在正式使用中应该去掉,换成长时间空闲的处理方法
                MessageBox.Show("长时间离开电脑", "警告", MessageBoxButtons.OK);
            }
        }

        // 检测前台进程
        void CheckAppInForeground(int updateTime = 60)
        {
            IntPtr handle = GetForegroundWindow();
            UInt32 processID;
            GetWindowThreadProcessId(handle, out processID);
            Process ps = Process.GetProcessById((int)processID);
            // 查找是否是需要计时的进程,如果是,那么就将进程位于前台的时间加上间隔时间
            if (dictProcess.ContainsKey(ps.ProcessName) && dictProcess[ps.ProcessName].IsRecoderActiveTime)
            {
                dictProcess[ps.ProcessName].ActiveTime += updateTime;
                LogHelper.Instance.WriteLog("ProessName: " + ps.ProcessName + " ActiveTime:" + dictProcess[ps.ProcessName].ActiveTime);
            }
        }

        // 检测定时器定时运行逻辑
        private void timerCheck_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            /*
            * 在这个检测中最好加上只在工作时间进行检测就行
            */
            CheckMouseKeyboard();
            CheckAppInForeground(20);
        }

        // 配置重载定时器定时运行逻辑
        private void timerReloadConfig_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            ConfigHelper.Instance.LoadConfig();
        }
    }
}
